/**
 * 一个基于 plupload (mOxie) 的上传工具 (上传前 md5) 及 缩略图
 * <a href="javascript:void(0);" class="js-plupload" data-
 */
;
(function ($, plupload, o) {
	var ENTRY_TPL = '<div class="upload-entry" id="{id}-entry" style="/*width: 307px;*/">' +
			// '<b class="upload-entry-icon"></b>' +
			'<div class="upload-entry-icon"></div>' +
			'<div class="upload-entry-name" title="{name}">{name}</div>' +
			'<div class="upload-op"></div>' +
			'<div class="upload-entry-status">待上传，请稍候..</div>' +
			'<div class="upload-tip" style="display: none;"></div>' +
			'</div>',
		PROGRESS_TPL = '<div class="upload-progress">' +
			'<span class="upload-progress-bar">' +
			'<span class="upload-progress-bar-inner" style="width:{percent}%;"></span>' +
			'</span>' +
			'<span class="upload-progress-text">' +
			'{percent}%,  {remainTime}' +
			'</span>' +
			'</div>',
		BTN_TPL = '<decode href="javascript:void(0);"></decode>';

	var FILE_COUNT_ERROR = -9001, // 文件数量错误
		MODE_LIST = 'l',  // 列表模式 - 列表
		MODE_THUMB = 't'; // 列表模式 - 缩略图

	var defaults = {
		runtimes: 'html5,flash,silverlight', // 'html5,flash,silverlight,html4
		file_data_name: 'file',
		url: undefined,  // must be
		multi_selection: true,
		browse_button: undefined, // must be
		drop_element: undefined,
		browse_button_active: 'active',
		browse_button_hover: 'hover',
		filters: {
			mime_types: [
				{title: 'Allowd Files', extensions: '*'}
			]
		},
		// container: undefined,
		// multipart_params: {}
		// custom
		name: null, /* 创建表单域名称 */
		value: null, /* 服务器相应属性值,作为表单域值 */
		list: '', /* 列表容器 */
		mode: MODE_LIST,
		auto: true, /* 自动开始 */
		max_file_size: '5G',
		max_file_count: '50'
	};

	function Uploader(settings) {
		var me = this,
			filters = settings && settings.filters,
			uploader, btn;

		if (!(me instanceof Uploader)) {
			return new Uploader(settings);
		}


		settings = o.extend({}, defaults, settings || {});
		filters && (settings['filters'] = filters);

		o.extend(settings['filters'], {
			max_file_size: settings['max_file_size'],
			max_file_count: settings['max_file_count']
		});
		delete settings['max_file_size'];
		delete settings['max_file_count'];

		if (settings['list']) {
			settings['list'] = o.get(settings['list']);
		}

		btn = o.get(settings['browse_button']);
		if (btn && btn.uploader && 'function' === typeof btn.uploader.destroy) {
			btn.uploader.destroy();
		}

		uploader = new plupload.Uploader(settings);
		uploader.bind('PostInit', onInit);
		uploader.bind('FilesRemoved', onRemoved);
		uploader.bind('FileFiltered', onFiltered);
		uploader.bind('UploadFile', onUpload);
		uploader.bind('UploadProgress', onProgress);
		uploader.bind('FileUploaded', onUploaded);
		uploader.bind('Error', onError);
		uploader.init();
	}

	/**
	 * initialize
	 */
	function onInit(up) {
		o.each(up.getOption('browse_button'), function (btn) {
			btn.uploader = up;
		});
	}

	/**
	 * remove files from uploader
	 */
	function onRemoved(up, files) {
		up.disableBrowse(false);
		o.each(up.getOption('browse_button'), function (btn) {
			btn.disabled = false;
		});

		// remove ui element
		o.each(files, function (f) {
			$('#' + f.id + '-entry').remove();
		});
	}

	/**
	 * added to uploader
	 */
	function onFiltered(up, file) {
		var container = up.getOption('list'),
			mode = up.getOption('mode'),
			auto = up.getOption('auto');

		if (container) {
			var $entry = $(renderTpl(ENTRY_TPL, file));

			$entry.hover(function () {
				$entry.addClass('hover');
			}, function () {
				$entry.removeClass('hover');
			});

			$(BTN_TPL).html('删除').click(function (event) {
				up.removeFile(file);
				$entry.fadeOut('normal', function () {
					$entry.remove();
				});

				stopPropagation(event)
			}).appendTo($entry.find('.upload-op').html(''));

			$(container).append($entry);

			// 缩略图
			if (MODE_THUMB == mode) {
				var wrap = $entry.find('.upload-entry-icon')[0];

				$entry.addClass('upload-thumb');

				// embed thumb
				var img = new o.Image;
				img.bind('load', function () {
					if (!file.getSource().rendered) { // 如果没有渲染服务端图片才渲染
						img.embed(wrap, { /*width: 110, height: 110,*/ crop: true });
					}
				});
				img.bind('error', function () {
					$(wrap).html('<span style="color:red;margin-top:50%;/*top: -10px;*/width:100%;text-align:center;position: absolute;">error</span>');
				});

				img.load(file.getSource());
			}

			// hash(file.getSource());
		}

		if (1 == auto || true == auto) {
			up.start();
		}
	}

	/**
	 * start upload file
	 */
	function onUpload(up, file) {
		var $entry = $('#' + file.id + '-entry');

		$(BTN_TPL).html('取消').click(function (event) {
			up.removeFile(file);

			$entry.fadeOut('normal', function () {
				$entry.remove();
			});

			stopPropagation(event);
		}).appendTo($entry.find('.upload-op').html(''));
	}

	/**
	 * upload progress
	 */
	function onProgress(up, file) {
		var $status = $('#' + file.id + '-entry .upload-entry-status'),
			$tip = $('#' + file.id + '-entry .upload-tip'),

			now = new Date().getTime(),
			time = now - (file.startTime || 0),
			speed, remain;

		file.startTime = file.startTime || now;

		if (100 != file.percent) {
			speed = file.loaded / time;
			remain = Math.round((file.size - file.loaded) / speed);

			file.remainTime = speed = formatSize(speed * 1E3) + '/S';
			file.remainTime = formatTime(Math.round(remain / 1E3));
			$status.html(renderTpl(PROGRESS_TPL, file));
			$tip.show().html(speed);
		} else {
			delete file.startTime;
			delete file.remainTime;
		}
	}

	/**
	 * upload file success
	 */
	function onUploaded(up, file, result) {
		var name = up.getOption('name'),
			value = up.getOption('value'),
			mode = up.getOption('mode'),
			data = $.parseJSON(result.response);

		if (data.error) {
			return;
		}

		// upload success
		var $entry = $('#' + file.id + '-entry'),
			$delBtn = $(BTN_TPL).html('删除').click(function (event) {
				up.removeFile(file);

				$entry.fadeOut('normal', function () {
					$entry.remove();
				});
				stopPropagation(event);
			});

		$entry.find('.upload-entry-status').html(formatSize(file.size).toUpperCase() + ' <span class="nui-txt-suc">上传完成</span>');
		$entry.find('.upload-op').html('').append($delBtn);

		// 如果设置了名称且提供了值字段, 则创建相应隐藏域
		if (name && value) {
			$('<input type="hidden">').attr('name', name).val(data[value]).appendTo($entry);
		}

		if ('thumb' == mode) {
			var $container = $entry.find('.upload-entry-icon'),
				img = new Image(),
				size = o.getSize($container[0]);

			img.onload = function () {
				file.getSource().rendered = true;
				img.style.width = size.w + 'px'; //'100%';
				img.style.height = size.h + 'px'; //'100%';
				$container.html('').append(img);
			};
			img.src = data[value]; // + ('img' == uploadName ? '_' + size.w + 'x' + size.h : '');
		}
	}

	function onError(up, err) {
		// do not show UI if no runtime can be initialized
		if (plupload.INIT_ERROR == err.code) {
			var name = up.getOption('name');
			setTimeout(function () {
				o.each(up.getOption('browse_button') || [], function (btn) {
					name && btn.setAttribute('name', name);
					delete btn.uploader;
				});
				uploader.destroy();
			}, 1);

			return;
		}

		if (plupload.HTTP_ERROR == err.code) {
			var $entry = $('#' + err.file.id + '-entry');
			var $op = $entry.find('.upload-op').html('');
			var $btn = $(BTN_TPL).html('重试').appendTo($op).click(function (event) {
				var file = err.file;
				file.status = plupload.QUEUED;
				uploader.start();

				stopPropagation(event)
			});
			$('<span>|</span>').appendTo($op);
			$(BTN_TPL).html('删除').appendTo($op).click(function (event) {
				$entry.fadeOut('normal', $.proxy($entry.remove, $entry));
				stopPropagation(event);
			});

			$entry.find('.upload-entry-status').html('<span style="color: #ff1717;">'+ err.message +'</span>');

			return;
		}

		/*
		plupload.FILE_EXTENSION_ERROR:
		plupload.FILE_SIZE_ERROR:
		plupload.FILE_DUPLICATE_ERROR:
		plupload.IMAGE_FORMAT_ERROR :
		FILE_COUNT_ERROR:
		*/
		alert(err.message);  // 已经是国际化信息
	}

	function stopPropagation(event) {
		event = event || window.event;
		if (event.stopPropagation) {
			event.stopPropagation();
		} else {
			event.cancelBubble = true;
		}
	}

	/**
	 * file count filter
	 */
	plupload.addFileFilter('max_file_count', function (maxCount/* config */, file, cb) {
		var uploader = this,
		// currentQueueSize = uploader.files.length - uploader.total.uploaded - uploader.total.failed;
			currentQueueSize = uploader.files.length;

		if (maxCount > 1) {  // allowed count > 1
			if (maxCount == currentQueueSize + 1) {
				uploader.disableBrowse(true);
				o.each(uploader.getOption('browse_button'), function (el) {
					el.disabled = true;
				});
			}

			if (maxCount <= currentQueueSize) {
				uploader.trigger('Error', {
					code: FILE_COUNT_ERROR,
					message: "File count error.",
					file: file
				});
				cb(false);
			} else {
				cb(true); // allow add file
			}
		} else {
			// only one file
			o.each(uploader.files, function (f) {
				uploader.removeFile(f);
			});

			cb(true);   // allow added
		}
	});

	/**
	 * full mode:
	 * <button data-target="file-list:t:3*500m:fname@img-mgt/upload:url:1" data-suffix="jpg,png" data-drop-target="drop-zone">Upload<button>
	 * <button data-target=".:l:3*500m:fname@img-mgt/upload:url:1" data-suffix="jpg,png" data-drop-target="drop-zone">Upload<button>
	 * simple:
	 * <li data-target=".::1x30m:file::" data-suffix="zip"></li>
	 * <li data-target=".::1x30m:::" data-suffix="zip"></li>
	 * <p />
	 * data-target: container:mode:countxsize:uploadName@url:valueField:auto
	 *
	 * @param node
	 * @returns {Uploader}
	 */
	Uploader.embed = function (node) {
		var CONTEXT_PATH = (window.N || window)['contextPath'] || '',
			IMG_UPLOAD_URL = CONTEXT_PATH + '/storage/images/ul',
			FILE_UPLOAD_URL = CONTEXT_PATH + '/storage/files/ul',
			name = node.name,
			opts = node.getAttribute('data-target'),
			dropTarget = node.getAttribute('data-drop-target'),
			extensions = node.getAttribute('data-suffix') || '*',
			config = {},
			tmp;

		if (node.uploader && 'function' === typeof node.uploader.destroy) {
			name = name || node.uploader.getOption('name');
			node.uploader.destroy();
		}

		if (opts) {
			opts = opts.split(':');

			if (6 != opts.length) {
				throw new Error('Invalid config, data-target format: "[container]:[mode]:[count][xsize]:[upload_form_data_name][@url]:[valueField]:[auto]"');
			}

			opts[0] && (config['list'] = opts[0]);
			opts[1] && (config['mode'] = opts[1]);

			if (opts[2]) {
				tmp = opts[2].split('x').reverse();
				tmp[0] && (config['max_file_size'] = tmp[0]);
				tmp[1] && (config['max_file_count'] = tmp[1]);
			}

			config['form_data_name'] = 'file';
			if (opts[3]) {
				tmp = opts[3].split('@');
				tmp[0] && (config['form_data_name'] = tmp[0]);
				config['url'] = tmp[1];
			}
			tmp = config['form_data_name'];
			config['url'] = ('img' == tmp ? IMG_UPLOAD_URL : ('file' == tmp ? FILE_UPLOAD_URL : config['url']));

			config['value'] && (config['value'] = opts[4]);
			config['auto'] && (config['auto'] = '1' == opts[5] || 'true' == opts[5]);
		}

		if (config['list']) {
			config['list'] = ('.' == config['list'] ? node : ('..' == config['list'] ? node.parentNode : config['list']));
		}

		if (dropTarget) {
			dropTarget = ('.' == dropTarget ? node : ('..' == dropTarget ? node.parentNode : dropTarget));
		}

		o.extend(config, {
			browse_button: node,
			name: name,
			drop_element: dropTarget,
			filters: {
				mime_types: [
					{title: 'Allowed Files', extensions: extensions}
				]
			}
		});

		node.removeAttribute('name');
		return new Uploader(config);
	};

	window.Uploader = Uploader;
	$('.js-plupload').each(function (i, el) {
		Uploader.embed(el);
	});

	// -------------------------
	function renderTpl(tpl, data) {
		for (var p in data) {
			if (data.hasOwnProperty(p)) {
				tpl = tpl.replace(new RegExp('{' + p + '}', 'g'), data[p]);
			}
		}
		return tpl;
	}

	function formatTime(seconds) {
		var h = '00', m = '00', seconds = seconds || 0;

		if (seconds >= 60 * 60) {
			h = Math.round(seconds / 60 / 60);
			h = h > 9 ? h : '0' + h;
			seconds = seconds % (60 * 60);
		}

		if (seconds >= 60) {
			m = Math.round(seconds / 60);
			m = m > 9 ? m : '0' + m;
			seconds = seconds % 60;
		}

		seconds = seconds > 9 ? seconds : '0' + seconds;
		return h + ':' + m + ':' + seconds;
	}

	function formatSize(size, pointLength, units) {
		var unit;

		units = units || [ 'B', 'K', 'M', 'G', 'TB' ];

		while ((unit = units.shift()) && size > 1024) {
			size = size / 1024;
		}

		return (unit === 'B' ? size : size.toFixed(pointLength || 2)) + unit;
	}

	//
	function hash(file) {
		var chunkSize = 2097152,    // 2MB
			chunks = Math.ceil(file.size / chunkSize),
			currentChunk = 0,
		// reader = new FileReader(),
			reader = new o.FileReader,// oMixe.FileReader
			spark = new SparkMD5;

		if (!file.size) {    // not html5, not flash, not silverlight
			// console.log('can not read file size', file.size);
			file.hash = -1;
			return;
		}

		file.hash = 0;
		// render.onload = function() {} // html5
		reader.bind('loadend', function (e) {   // mOxie
			// console.log('read chunk nr:', currentChunk + 1, 'of', chunks);

			// console.log(e.target.error);
			if (e.target.error) {
				file.hash = -1;
				return;
			}
			spark.appendBinary(e.target.result);
			currentChunk++;

			if (currentChunk < chunks) {
				loadNextChunk();
			} else {
				// console.log('finished');
				file.hash = spark.end(); // warning: call onece only
				//console.warn('computed hash', file.hash);
			}

		});
		loadNextChunk();

		function loadNextChunk() {
			var start = currentChunk * chunkSize,
				end = start + chunkSize >= file.size ? file.size : start + chunkSize;

			/*
			 slice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice;
			 reader.readAsBinaryString(slice.call(file, start, end));
			 */
			reader.readAsBinaryString(file.slice(start, end));
		}
	}
})(jQuery, plupload, mOxie);



